<?php
# 定义一组路由规则
define('ROUTE_HTML_INDEX', '.html.index');
define('ROUTE_HTML_APP', '.html.app');
define('ROUTE_JSON_INDEX', '.json.index');
define('ROUTE_JSON_APP', '.json.app');
define('ROUTE_JSONP_INDEX', '.jsonp.index');
define('ROUTE_JSONP_APP', '.jsonp.app');
define('ROUTE_XML_INDEX', '.xml.index');
define('ROUTE_XML_APP', '.xml.app');
define('ROUTE_DO_APP', '.do.app');

# 定义供外部使用的路由规则key
define('ROUTE_APP_KEY', 'app');
define('ROUTE_Q_KEY', 'q');
define('ROUTE_DO_KEY', 'do');
define('ROUTE_SET_KEY', 'q:route');

# 定义缺省路由值
define('ROUTE_DEFAULT_APP', 'index');
define('ROUTE_DEFAULT_Q', 'index');
define('ROUTE_DEFAULT_DO', 'index');

final class Route
{
	
	private static $map = array(
		
		ROUTE_HTML_INDEX	=>array(
			'pattern' => '/{q}.html',
			'config' => array (
					ROUTE_Q_KEY => '[a-z][a-z0-9]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q 
			)
		),
		
		ROUTE_HTML_APP	=>array(
			'pattern' => '/{app}/{q}.html',
			'config' => array (
					ROUTE_APP_KEY => '[a-z][a-z0-9]+',
					ROUTE_Q_KEY => '[a-z][a-z0-9\/]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q 
			)
		),
				
		ROUTE_JSON_INDEX	=>array(
			'pattern' => '/{q}.json',
			'config' => array (
					ROUTE_Q_KEY => '[a-z][a-z0-9]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q  
			)
		),
		
		ROUTE_JSON_APP	=>array(
			'pattern' => '/{app}/{q}.json',
			'config' => array (
					ROUTE_APP_KEY => '[a-z][a-z0-9]+',
					ROUTE_Q_KEY => '[a-z][a-z0-9\/]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q  
			)
		),
				
		ROUTE_JSONP_INDEX	=>array(
			'pattern' => '/{q}.jsonp',
			'config' => array (
					ROUTE_Q_KEY => '[a-z][a-z0-9]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q  
			)
		),
		
		ROUTE_JSONP_APP	=>array(
			'pattern' => '/{app}/{q}.jsonp',
			'config' => array (
					ROUTE_APP_KEY => '[a-z][a-z0-9]+',
					ROUTE_Q_KEY => '[a-z][a-z0-9\/]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q  
			)
		),
				
		ROUTE_XML_INDEX	=>array(
			'pattern' => '/{q}.xml',
			'config' => array (
					ROUTE_Q_KEY => '[a-z][a-z0-9]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q  
			)
		),
		
		ROUTE_XML_APP	=>array(
			'pattern' => '/{app}/{q}.xml',
			'config' => array (
					ROUTE_APP_KEY => '[a-z][a-z0-9]+',
					ROUTE_Q_KEY => '[a-z][a-z0-9\/]+' 
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q 
			)
		),

		ROUTE_DO_APP	=>array(
			'pattern' => '/{app}/{q}!{do}.do',
			'config' => array (
					ROUTE_APP_KEY => '[a-z][a-z0-9]+',
					ROUTE_Q_KEY => '[a-z][a-z0-9\/]+',
					ROUTE_DO_KEY => '[a-z0-9\-\:\+]+',
			),
			'default' => array (
					ROUTE_APP_KEY => ROUTE_DEFAULT_APP,
					ROUTE_Q_KEY => ROUTE_DEFAULT_Q,
					ROUTE_DO_KEY => ROUTE_DEFAULT_DO,
			)
		),
	);
	
	
	/**
	 * 取得请求的 URI 信息（不含协议、主机名）
	 *
	 * 例如：
	 *
	 * http://m.x/admin/index.php?controller=test
	 *
	 * 返回：
	 *
	 * /admin/index.php?controller=test
	 *
	 * @return string
	 */
	static function get_request_uri()
	{
	    static $request_uri = null;
	    if (!is_null($request_uri)) return $request_uri;

	    if (isset($_SERVER['HTTP_X_REWRITE_URL']))
	    {
	        $request_uri = $_SERVER['HTTP_X_REWRITE_URL'];
	    }
	    elseif (isset($_SERVER['REQUEST_URI']))
	    {
	        $request_uri = $_SERVER['REQUEST_URI'];
	    }
	    elseif (isset($_SERVER['ORIG_PATH_INFO']))
	    {
	        $request_uri = $_SERVER['ORIG_PATH_INFO'];
	        if (!empty($_SERVER['QUERY_STRING']))
	        {
	            $request_uri .= '?' . $_SERVER['QUERY_STRING'];
	        }
	    }
	    else
	    {
	        $request_uri = '';
	    }

	    return $request_uri;
	}

	/**
	 * 取得请求的 URI 信息（不含协议、主机名、查询参数、PATHINFO）
	 *
	 * 例如：
	 *
	 * http://m.x/admin/index.php?controller=test
	 * http://m.x/admin/index.php/path/to
	 *
	 * 都返回：
	 *
	 * /admin/index.php
	 *
	 * @return string
	 */
	static function get_request_baseuri()
	{
	    static $request_base_uri = null;
	    if (!is_null($request_base_uri)) return $request_base_uri;

	    $filename = basename($_SERVER['SCRIPT_FILENAME']);

	    if (basename($_SERVER['SCRIPT_NAME']) === $filename)
	    {
	        $url = $_SERVER['SCRIPT_NAME'];
	    }
	    elseif (basename($_SERVER['PHP_SELF']) === $filename)
	    {
	        $url = $_SERVER['PHP_SELF'];
	    }
	    elseif (isset($_SERVER['ORIG_SCRIPT_NAME']) && basename($_SERVER['ORIG_SCRIPT_NAME']) === $filename)
	    {
	        $url = $_SERVER['ORIG_SCRIPT_NAME']; // 1and1 shared hosting compatibility
	    }
	    else
	    {
	        // Backtrack up the script_filename to find the portion matching
	        // php_self
	        $path = $_SERVER['PHP_SELF'];
	        $segs = explode('/', trim($_SERVER['SCRIPT_FILENAME'], '/'));
	        $segs = array_reverse($segs);
	        $index = 0;
	        $last = count($segs);
	        $url = '';
	        do
	        {
	            $seg = $segs[$index];
	            $url = '/' . $seg . $url;
	            ++ $index;
	        } while (($last > $index) && (false !== ($pos = strpos($path, $url))) && (0 != $pos));
	    }

	    // Does the baseUrl have anything in common with the request_uri?
	    $request_uri = self::get_request_uri();

	    if (0 === strpos($request_uri, $url))
	    {
	        // full $url matches
	        $request_base_uri = $url;
	        return $request_base_uri;
	    }

	    if (0 === strpos($request_uri, dirname($url)))
	    {
	        // directory portion of $url matches
	        $request_base_uri = rtrim(dirname($url), '/') . '/';
	        return $request_base_uri;
	    }

	    if (! strpos($request_uri, basename($url)))
	    {
	        // no match whatsoever; set it blank
	        $request_base_uri = '';
	        return '';
	    }

	    // If using mod_rewrite or ISAPI_Rewrite strip the script filename
	    // out of baseUrl. $pos !== 0 makes sure it is not matching a value
	    // from PATH_INFO or QUERY_STRING
	    if ((strlen($request_uri) >= strlen($url))
	        && ((false !== ($pos = strpos($request_uri, $url)))
	        && ($pos !== 0)))
	    {
	        $url = substr($request_uri, 0, $pos + strlen($url));
	    }

	    $request_base_uri = rtrim($url, '/') . '/';
	    return $request_base_uri;
	}

	/**
	 * 取得响应请求的 .php 文件在 URL 中的目录部分
	 *
	 * 例如：
	 *
	 * http://m.x/admin/index.php?controller=test
	 *
	 * 返回：
	 *
	 * /admin/
	 *
	 * @return string
	 */
	static function get_request_dir()
	{
	    static $dir = null;
	    
	    $base_uri = self::get_request_baseuri();
	    if (substr($base_uri, - 1, 1) == '/')
	    {
	        $dir = $base_uri;
	    }
	    else
	    {
	        $dir = dirname($base_uri);
	    }

	    $dir = rtrim($dir, '/\\') . '/';
	    return $dir;    
	}

	/**
	 * 返回 PATHINFO 信息
	 *
	 * 例如：
	 *
	 * http://m.x/admin/index.php/path/to
	 *
	 * 返回：
	 *
	 * /path/to
	 *
	 * @return string
	 */
	static function get_request_pathinfo()
	{
	    static $pathinfo = null;
	    if (!is_null($pathinfo)) return $pathinfo;
	    
	    if (!empty($_SERVER['PATH_INFO'])) 
	    {
	        $pathinfo = $_SERVER['PATH_INFO'];
	        return $pathinfo;
	    }

	    $base_url = self::get_request_baseuri();

	    if (null === ($request_uri = self::get_request_uri())) return '';

	    // Remove the query string from REQUEST_URI
	    if (($pos = strpos($request_uri, '?')))
	    {
	        $request_uri = substr($request_uri, 0, $pos);
	    }

	    if ((null !== $base_url) && (false === ($pathinfo = substr($request_uri, strlen($base_url)))))
	    {
	        // If substr() returns false then PATH_INFO is set to an empty string
	        $pathinfo = '';
	    }
	    elseif (null === $base_url)
	    {
	        $pathinfo = $request_uri;
	    }
	    return $pathinfo;
	}

	static function init()
	{
		self::parse_url(self::get_request_pathinfo());
	}
	
	static function parse_url( $pathinfo )
	{
		if ( empty($pathinfo) ) $pathinfo = '/';
		$pathinfo = '/' . ltrim($pathinfo,'\/');
		
        foreach(self::$map AS $key => $route)
        {
            # 将路由的配置参数添加到 正则规则中.
            foreach($route['config'] AS $ck => $cval)
            {
                $route['pattern'] = str_replace('{'.$ck.'}', '('.$cval.')', $route['pattern']);
            }

            if (preg_match('#^'.$route['pattern'].'/?$#i', $pathinfo, $match_result))
            {
                # 处理默认项
                if ( !empty($route['default']) )
                {
                    foreach ($route['default'] as $ck => $cval)
                    {
                        $_GET[$ck] = $_REQUEST[$ck] = $cval;
                    }
                }

                # offset 为0 是 原字符串
                $offset = 1;
                foreach($route['config'] as $ck => $cval)
                {
                    if(isset($match_result[$offset]))
                    {
                        $_GET[$ck] = $_REQUEST[$ck] = $match_result[$offset++];
                    }
                }

				$_GET[ROUTE_Q_KEY] = str_replace('/', '.', trim( $_GET[ROUTE_Q_KEY], "\/" ));
                # 定位匹配的规则
                switch ($key) {
                	case ROUTE_HTML_INDEX:
                	case ROUTE_HTML_APP:
                	case ROUTE_JSON_INDEX:
                	case ROUTE_JSON_APP:
                	case ROUTE_XML_INDEX:
                	case ROUTE_XML_APP:
                	case ROUTE_JSONP_INDEX:
                	case ROUTE_JSONP_APP:
                	case ROUTE_DO_APP:
                		$_GET[ROUTE_SET_KEY] = $key;
                		break;
                }

                break;
            }
        }

        if ( empty($_GET[ROUTE_APP_KEY]) ) $_GET[ROUTE_APP_KEY] = ROUTE_DEFAULT_APP;
        if ( empty($_GET[ROUTE_Q_KEY]) ) $_GET[ROUTE_Q_KEY] = ROUTE_DEFAULT_Q;
        if ( empty($_GET[ROUTE_SET_KEY]) ) $_GET[ROUTE_SET_KEY] = ROUTE_HTML_INDEX;
	}

	/**
     * 生成 URL Rewrite 模式的网址
     *
     * @param string $route_key
     * @param array $params
     *
     * @return string
     */
    static function rewrite_url($route_key, $params)
    {
        $route = empty(self::$map[$route_key]) ? self::$map[ROUTE_HTML_INDEX] : self::$map[$route_key];
        
        # 找出要参数化的变量(将参数数组同default合并起来)
        $kv = array();
        foreach($route['config'] as $ck => $cval)
        {
            $kv[$ck] = $cval;
        }
        foreach($route['default'] as $ck => $cval)
        {
            $kv[$ck] = $cval;
        }
        
        # 填充必须的参数
        if ( empty($kv[ROUTE_APP_KEY]) )
        {
            $kv[ROUTE_APP_KEY] = ROUTE_DEFAULT_APP;
        }
        if ( empty($kv[ROUTE_Q_KEY]) )
        {
            $kv[ROUTE_Q_KEY] = ROUTE_DEFAULT_Q;
        }
        if ( !empty($params[ROUTE_Q_KEY]) )
        {
        	$params[ROUTE_Q_KEY] = str_replace('.', '/', trim($params[ROUTE_Q_KEY], "\." ));
        }

        foreach($kv as $ck => $cval)
        {
            if ( !empty($params[$ck]) )
            {
                $cval = $params[$ck];
                unset($params[$ck]);
            }
            $route['pattern'] = str_replace('{'.$ck.'}', $cval, $route['pattern']);
        }

        $url = rtrim(self::get_request_baseuri(), '\/') . '/' . ltrim($route['pattern'], '\/');

        if (!empty($params))
        {
            $url .= '?' . http_build_query($params);
        }

        return $url;
    }

}
